home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Archive / Tar.php
PHP Script  |  2004-10-01  |  59KB  |  1,661 lines

  1. <?php
  2. /* vim: set ts=4 sw=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at the following url:           |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Vincent Blavet <vincent@blavet.net>                          |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Tar.php,v 1.19 2004/05/08 09:56:26 vblavet Exp $
  20.  
  21. require_once 'PEAR.php';
  22.  
  23.  
  24. define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
  25.  
  26. /**
  27. * Creates a (compressed) Tar archive
  28. *
  29. * @author   Vincent Blavet <vincent@blavet.net>
  30. * @version  $Revision: 1.19 $
  31. * @package  Archive
  32. */
  33. class Archive_Tar extends PEAR
  34. {
  35.     /**
  36.     * @var string Name of the Tar
  37.     */
  38.     var $_tarname='';
  39.  
  40.     /**
  41.     * @var boolean if true, the Tar file will be gzipped
  42.     */
  43.     var $_compress=false;
  44.  
  45.     /**
  46.     * @var string Type of compression : 'none', 'gz' or 'bz2'
  47.     */
  48.     var $_compress_type='none';
  49.  
  50.     /**
  51.     * @var string Explode separator
  52.     */
  53.     var $_separator=' ';
  54.  
  55.     /**
  56.     * @var file descriptor
  57.     */
  58.     var $_file=0;
  59.  
  60.     /**
  61.     * @var string Local Tar name of a remote Tar (http:// or ftp://)
  62.     */
  63.     var $_temp_tarname='';
  64.  
  65.     // {{{ constructor
  66.     /**
  67.     * Archive_Tar Class constructor. This flavour of the constructor only
  68.     * declare a new Archive_Tar object, identifying it by the name of the
  69.     * tar file.
  70.     * If the compress argument is set the tar will be read or created as a
  71.     * gzip or bz2 compressed TAR file.
  72.     *
  73.     * @param    string  $p_tarname  The name of the tar archive to create
  74.     * @param    string  $p_compress can be null, 'gz' or 'bz2'. This
  75.     *                   parameter indicates if gzip or bz2 compression
  76.     *                   is required.  For compatibility reason the
  77.     *                   boolean value 'true' means 'gz'.
  78.     * @access public
  79.     */
  80.     function Archive_Tar($p_tarname, $p_compress = null)
  81.     {
  82.         $this->PEAR();
  83.         $this->_compress = false;
  84.         $this->_compress_type = 'none';
  85.         if ($p_compress === null) {
  86.             if (@file_exists($p_tarname)) {
  87.                 if ($fp = @fopen($p_tarname, "rb")) {
  88.                     // look for gzip magic cookie
  89.                     $data = fread($fp, 2);
  90.                     fclose($fp);
  91.                     if ($data == "\37\213") {
  92.                         $this->_compress = true;
  93.                         $this->_compress_type = 'gz';
  94.                     // No sure it's enought for a magic code ....
  95.                     } elseif ($data == "BZ") {
  96.                         $this->_compress = true;
  97.                         $this->_compress_type = 'bz2';
  98.                     }
  99.                 }
  100.             } else {
  101.                 // probably a remote file or some file accessible
  102.                 // through a stream interface
  103.                 if (substr($p_tarname, -2) == 'gz') {
  104.                     $this->_compress = true;
  105.                     $this->_compress_type = 'gz';
  106.                 } elseif ((substr($p_tarname, -3) == 'bz2') ||
  107.                           (substr($p_tarname, -2) == 'bz')) {
  108.                     $this->_compress = true;
  109.                     $this->_compress_type = 'bz2';
  110.                 }
  111.             }
  112.         } else {
  113.             if (($p_compress === true) || ($p_compress == 'gz')) {
  114.                 $this->_compress = true;
  115.                 $this->_compress_type = 'gz';
  116.             } else if ($p_compress == 'bz2') {
  117.                 $this->_compress = true;
  118.                 $this->_compress_type = 'bz2';
  119.             }
  120.         }
  121.         $this->_tarname = $p_tarname;
  122.         if ($this->_compress) { // assert zlib or bz2 extension support
  123.             if ($this->_compress_type == 'gz')
  124.                 $extname = 'zlib';
  125.             else if ($this->_compress_type == 'bz2')
  126.                 $extname = 'bz2';
  127.  
  128.             if (!extension_loaded($extname)) {
  129.                 PEAR::loadExtension($extname);
  130.             }
  131.             if (!extension_loaded($extname)) {
  132.                 die("The extension '$extname' couldn't be found.\n".
  133.                     "Please make sure your version of PHP was built ".
  134.                     "with '$extname' support.\n");
  135.                 return false;
  136.             }
  137.         }
  138.     }
  139.     // }}}
  140.  
  141.     // {{{ destructor
  142.     function _Archive_Tar()
  143.     {
  144.         $this->_close();
  145.         // ----- Look for a local copy to delete
  146.         if ($this->_temp_tarname != '')
  147.             @unlink($this->_temp_tarname);
  148.         $this->_PEAR();
  149.     }
  150.     // }}}
  151.  
  152.     // {{{ create()
  153.     /**
  154.     * This method creates the archive file and add the files / directories
  155.     * that are listed in $p_filelist.
  156.     * If a file with the same name exist and is writable, it is replaced
  157.     * by the new tar.
  158.     * The method return false and a PEAR error text.
  159.     * The $p_filelist parameter can be an array of string, each string
  160.     * representing a filename or a directory name with their path if
  161.     * needed. It can also be a single string with names separated by a
  162.     * single blank.
  163.     * For each directory added in the archive, the files and
  164.     * sub-directories are also added.
  165.     * See also createModify() method for more details.
  166.     *
  167.     * @param array  $p_filelist An array of filenames and directory names, or a single
  168.     *                           string with names separated by a single blank space.
  169.     * @return                   true on success, false on error.
  170.     * @see createModify()
  171.     * @access public
  172.     */
  173.     function create($p_filelist)
  174.     {
  175.         return $this->createModify($p_filelist, '', '');
  176.     }
  177.     // }}}
  178.  
  179.     // {{{ add()
  180.     /**
  181.     * This method add the files / directories that are listed in $p_filelist in
  182.     * the archive. If the archive does not exist it is created.
  183.     * The method return false and a PEAR error text.
  184.     * The files and directories listed are only added at the end of the archive,
  185.     * even if a file with the same name is already archived.
  186.     * See also createModify() method for more details.
  187.     *
  188.     * @param array  $p_filelist An array of filenames and directory names, or a single
  189.     *                           string with names separated by a single blank space.
  190.     * @return                   true on success, false on error.
  191.     * @see createModify()
  192.     * @access public
  193.     */
  194.     function add($p_filelist)
  195.     {
  196.         return $this->addModify($p_filelist, '', '');
  197.     }
  198.     // }}}
  199.  
  200.     // {{{ extract()
  201.     function extract($p_path='')
  202.     {
  203.         return $this->extractModify($p_path, '');
  204.     }
  205.     // }}}
  206.  
  207.     // {{{ listContent()
  208.     function listContent()
  209.     {
  210.         $v_list_detail = array();
  211.  
  212.         if ($this->_openRead()) {
  213.             if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  214.                 unset($v_list_detail);
  215.                 $v_list_detail = 0;
  216.             }
  217.             $this->_close();
  218.         }
  219.  
  220.         return $v_list_detail;
  221.     }
  222.     // }}}
  223.  
  224.     // {{{ createModify()
  225.     /**
  226.     * This method creates the archive file and add the files / directories
  227.     * that are listed in $p_filelist.
  228.     * If the file already exists and is writable, it is replaced by the
  229.     * new tar. It is a create and not an add. If the file exists and is
  230.     * read-only or is a directory it is not replaced. The method return
  231.     * false and a PEAR error text.
  232.     * The $p_filelist parameter can be an array of string, each string
  233.     * representing a filename or a directory name with their path if
  234.     * needed. It can also be a single string with names separated by a
  235.     * single blank.
  236.     * The path indicated in $p_remove_dir will be removed from the
  237.     * memorized path of each file / directory listed when this path
  238.     * exists. By default nothing is removed (empty path '')
  239.     * The path indicated in $p_add_dir will be added at the beginning of
  240.     * the memorized path of each file / directory listed. However it can
  241.     * be set to empty ''. The adding of a path is done after the removing
  242.     * of path.
  243.     * The path add/remove ability enables the user to prepare an archive
  244.     * for extraction in a different path than the origin files are.
  245.     * See also addModify() method for file adding properties.
  246.     *
  247.     * @param array  $p_filelist     An array of filenames and directory names, or a single
  248.     *                               string with names separated by a single blank space.
  249.     * @param string $p_add_dir      A string which contains a path to be added to the
  250.     *                               memorized path of each element in the list.
  251.     * @param string $p_remove_dir   A string which contains a path to be removed from
  252.     *                               the memorized path of each element in the list, when
  253.     *                               relevant.
  254.     * @return boolean               true on success, false on error.
  255.     * @access public
  256.     * @see addModify()
  257.     */
  258.     function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
  259.     {
  260.         $v_result = true;
  261.  
  262.         if (!$this->_openWrite())
  263.             return false;
  264.  
  265.         if ($p_filelist != '') {
  266.             if (is_array($p_filelist))
  267.                 $v_list = $p_filelist;
  268.             elseif (is_string($p_filelist))
  269.                 $v_list = explode($this->_separator, $p_filelist);
  270.             else {
  271.                 $this->_cleanFile();
  272.                 $this->_error('Invalid file list');
  273.                 return false;
  274.             }
  275.  
  276.             $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  277.         }
  278.  
  279.         if ($v_result) {
  280.             $this->_writeFooter();
  281.             $this->_close();
  282.         } else
  283.             $this->_cleanFile();
  284.  
  285.         return $v_result;
  286.     }
  287.     // }}}
  288.  
  289.     // {{{ addModify()
  290.     /**
  291.     * This method add the files / directories listed in $p_filelist at the
  292.     * end of the existing archive. If the archive does not yet exists it
  293.     * is created.
  294.     * The $p_filelist parameter can be an array of string, each string
  295.     * representing a filename or a directory name with their path if
  296.     * needed. It can also be a single string with names separated by a
  297.     * single blank.
  298.     * The path indicated in $p_remove_dir will be removed from the
  299.     * memorized path of each file / directory listed when this path
  300.     * exists. By default nothing is removed (empty path '')
  301.     * The path indicated in $p_add_dir will be added at the beginning of
  302.     * the memorized path of each file / directory listed. However it can
  303.     * be set to empty ''. The adding of a path is done after the removing
  304.     * of path.
  305.     * The path add/remove ability enables the user to prepare an archive
  306.     * for extraction in a different path than the origin files are.
  307.     * If a file/dir is already in the archive it will only be added at the
  308.     * end of the archive. There is no update of the existing archived
  309.     * file/dir. However while extracting the archive, the last file will
  310.     * replace the first one. This results in a none optimization of the
  311.     * archive size.
  312.     * If a file/dir does not exist the file/dir is ignored. However an
  313.     * error text is send to PEAR error.
  314.     * If a file/dir is not readable the file/dir is ignored. However an
  315.     * error text is send to PEAR error.
  316.     *
  317.     * @param array      $p_filelist     An array of filenames and directory names, or a single
  318.     *                                   string with names separated by a single blank space.
  319.     * @param string     $p_add_dir      A string which contains a path to be added to the
  320.     *                                   memorized path of each element in the list.
  321.     * @param string     $p_remove_dir   A string which contains a path to be removed from
  322.     *                                   the memorized path of each element in the list, when
  323.     *                                   relevant.
  324.     * @return                           true on success, false on error.
  325.     * @access public
  326.     */
  327.     function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
  328.     {
  329.         $v_result = true;
  330.  
  331.         if (!@is_file($this->_tarname))
  332.             $v_result = $this->createModify($p_filelist, $p_add_dir, $p_remove_dir);
  333.         else {
  334.             if (is_array($p_filelist))
  335.                 $v_list = $p_filelist;
  336.             elseif (is_string($p_filelist))
  337.                 $v_list = explode($this->_separator, $p_filelist);
  338.             else {
  339.                 $this->_error('Invalid file list');
  340.                 return false;
  341.             }
  342.  
  343.             $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  344.         }
  345.  
  346.         return $v_result;
  347.     }
  348.     // }}}
  349.  
  350.     // {{{ addString()
  351.     /**
  352.     * This method add a single string as a file at the
  353.     * end of the existing archive. If the archive does not yet exists it
  354.     * is created.
  355.     *
  356.     * @param string     $p_filename     A string which contains the full filename path
  357.     *                                   that will be associated with the string.
  358.     * @param string     $p_string       The content of the file added in the archive.
  359.     * @return                           true on success, false on error.
  360.     * @access public
  361.     */
  362.     function addString($p_filename, $p_string)
  363.     {
  364.         $v_result = true;
  365.  
  366.         if (!@is_file($this->_tarname)) {
  367.             if (!$this->_openWrite()) {
  368.                 return false;
  369.             }
  370.             $this->_close();
  371.         }
  372.         
  373.         if (!$this->_openAppend())
  374.             return false;
  375.  
  376.         // Need to check the get back to the temporary file ? ....
  377.         $v_result = $this->_addString($p_filename, $p_string);
  378.  
  379.         $this->_writeFooter();
  380.  
  381.         $this->_close();
  382.  
  383.         return $v_result;
  384.     }
  385.     // }}}
  386.  
  387.     // {{{ extractModify()
  388.     /**
  389.     * This method extract all the content of the archive in the directory
  390.     * indicated by $p_path. When relevant the memorized path of the
  391.     * files/dir can be modified by removing the $p_remove_path path at the
  392.     * beginning of the file/dir path.
  393.     * While extracting a file, if the directory path does not exists it is
  394.     * created.
  395.     * While extracting a file, if the file already exists it is replaced
  396.     * without looking for last modification date.
  397.     * While extracting a file, if the file already exists and is write
  398.     * protected, the extraction is aborted.
  399.     * While extracting a file, if a directory with the same name already
  400.     * exists, the extraction is aborted.
  401.     * While extracting a directory, if a file with the same name already
  402.     * exists, the extraction is aborted.
  403.     * While extracting a file/directory if the destination directory exist
  404.     * and is write protected, or does not exist but can not be created,
  405.     * the extraction is aborted.
  406.     * If after extraction an extracted file does not show the correct
  407.     * stored file size, the extraction is aborted.
  408.     * When the extraction is aborted, a PEAR error text is set and false
  409.     * is returned. However the result can be a partial extraction that may
  410.     * need to be manually cleaned.
  411.     *
  412.     * @param string $p_path         The path of the directory where the files/dir need to by
  413.     *                               extracted.
  414.     * @param string $p_remove_path  Part of the memorized path that can be removed if
  415.     *                               present at the beginning of the file/dir path.
  416.     * @return boolean               true on success, false on error.
  417.     * @access public
  418.     * @see extractList()
  419.     */
  420.     function extractModify($p_path, $p_remove_path)
  421.     {
  422.         $v_result = true;
  423.         $v_list_detail = array();
  424.  
  425.         if ($v_result = $this->_openRead()) {
  426.             $v_result = $this->_extractList($p_path, $v_list_detail, "complete", 0, $p_remove_path);
  427.             $this->_close();
  428.         }
  429.  
  430.         return $v_result;
  431.     }
  432.     // }}}
  433.  
  434.     // {{{ extractInString()
  435.     /**
  436.     * This method extract from the archive one file identified by $p_filename.
  437.     * The return value is a string with the file content, or NULL on error.
  438.     * @param string $p_filename     The path of the file to extract in a string.
  439.     * @return                       a string with the file content or NULL.
  440.     * @access public
  441.     */
  442.     function extractInString($p_filename)
  443.     {
  444.         if ($this->_openRead()) {
  445.             $v_result = $this->_extractInString($p_filename);
  446.             $this->_close();
  447.         } else {
  448.             $v_result = NULL;
  449.         }
  450.  
  451.         return $v_result;
  452.     }
  453.     // }}}
  454.  
  455.     // {{{ extractList()
  456.     /**
  457.     * This method extract from the archive only the files indicated in the
  458.     * $p_filelist. These files are extracted in the current directory or
  459.     * in the directory indicated by the optional $p_path parameter.
  460.     * If indicated the $p_remove_path can be used in the same way as it is
  461.     * used in extractModify() method.
  462.     * @param array  $p_filelist     An array of filenames and directory names, or a single
  463.     *                               string with names separated by a single blank space.
  464.     * @param string $p_path         The path of the directory where the files/dir need to by
  465.     *                               extracted.
  466.     * @param string $p_remove_path  Part of the memorized path that can be removed if
  467.     *                               present at the beginning of the file/dir path.
  468.     * @return                       true on success, false on error.
  469.     * @access public
  470.     * @see extractModify()
  471.     */
  472.     function extractList($p_filelist, $p_path='', $p_remove_path='')
  473.     {
  474.         $v_result = true;
  475.         $v_list_detail = array();
  476.  
  477.         if (is_array($p_filelist))
  478.             $v_list = $p_filelist;
  479.         elseif (is_string($p_filelist))
  480.             $v_list = explode($this->_separator, $p_filelist);
  481.         else {
  482.             $this->_error('Invalid string list');
  483.             return false;
  484.         }
  485.  
  486.         if ($v_result = $this->_openRead()) {
  487.             $v_result = $this->_extractList($p_path, $v_list_detail, "partial", $v_list, $p_remove_path);
  488.             $this->_close();
  489.         }
  490.  
  491.         return $v_result;
  492.     }
  493.     // }}}
  494.  
  495.     // {{{ setAttribute()
  496.     /**
  497.     * This method set specific attributes of the archive. It uses a variable
  498.     * list of parameters, in the format attribute code + attribute values :
  499.     * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
  500.     * @param mixed $argv            variable list of attributes and values
  501.     * @return                       true on success, false on error.
  502.     * @access public
  503.     */
  504.     function setAttribute()
  505.     {
  506.         $v_result = true;
  507.         
  508.         // ----- Get the number of variable list of arguments
  509.         if (($v_size = func_num_args()) == 0) {
  510.             return true;
  511.         }
  512.         
  513.         // ----- Get the arguments
  514.         $v_att_list = &func_get_args();
  515.  
  516.         // ----- Read the attributes
  517.         $i=0;
  518.         while ($i<$v_size) {
  519.  
  520.             // ----- Look for next option
  521.             switch ($v_att_list[$i]) {
  522.                 // ----- Look for options that request a string value
  523.                 case ARCHIVE_TAR_ATT_SEPARATOR :
  524.                     // ----- Check the number of parameters
  525.                     if (($i+1) >= $v_size) {
  526.                         $this->_error('Invalid number of parameters for attribute ARCHIVE_TAR_ATT_SEPARATOR');
  527.                         return false;
  528.                     }
  529.  
  530.                     // ----- Get the value
  531.                     $this->_separator = $v_att_list[$i+1];
  532.                     $i++;
  533.                 break;
  534.  
  535.                 default :
  536.                     $this->_error('Unknow attribute code '.$v_att_list[$i].'');
  537.                     return false;
  538.             }
  539.  
  540.             // ----- Next attribute
  541.             $i++;
  542.         }
  543.  
  544.         return $v_result;
  545.     }
  546.     // }}}
  547.  
  548.     // {{{ _error()
  549.     function _error($p_message)
  550.     {
  551.         // ----- To be completed
  552.         $this->raiseError($p_message);
  553.     }
  554.     // }}}
  555.  
  556.     // {{{ _warning()
  557.     function _warning($p_message)
  558.     {
  559.         // ----- To be completed
  560.         $this->raiseError($p_message);
  561.     }
  562.     // }}}
  563.  
  564.     // {{{ _openWrite()
  565.     function _openWrite()
  566.     {
  567.         if ($this->_compress_type == 'gz')
  568.             $this->_file = @gzopen($this->_tarname, "wb");
  569.         else if ($this->_compress_type == 'bz2')
  570.             $this->_file = @bzopen($this->_tarname, "wb");
  571.         else if ($this->_compress_type == 'none')
  572.             $this->_file = @fopen($this->_tarname, "wb");
  573.         else
  574.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  575.  
  576.         if ($this->_file == 0) {
  577.             $this->_error('Unable to open in write mode \''.$this->_tarname.'\'');
  578.             return false;
  579.         }
  580.  
  581.         return true;
  582.     }
  583.     // }}}
  584.  
  585.     // {{{ _openRead()
  586.     function _openRead()
  587.     {
  588.         if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  589.  
  590.           // ----- Look if a local copy need to be done
  591.           if ($this->_temp_tarname == '') {
  592.               $this->_temp_tarname = uniqid('tar').'.tmp';
  593.               if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  594.                 $this->_error('Unable to open in read mode \''.$this->_tarname.'\'');
  595.                 $this->_temp_tarname = '';
  596.                 return false;
  597.               }
  598.               if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  599.                 $this->_error('Unable to open in write mode \''.$this->_temp_tarname.'\'');
  600.                 $this->_temp_tarname = '';
  601.                 return false;
  602.               }
  603.               while ($v_data = @fread($v_file_from, 1024))
  604.                   @fwrite($v_file_to, $v_data);
  605.               @fclose($v_file_from);
  606.               @fclose($v_file_to);
  607.           }
  608.  
  609.           // ----- File to open if the local copy
  610.           $v_filename = $this->_temp_tarname;
  611.  
  612.         } else
  613.           // ----- File to open if the normal Tar file
  614.           $v_filename = $this->_tarname;
  615.  
  616.         if ($this->_compress_type == 'gz')
  617.             $this->_file = @gzopen($v_filename, "rb");
  618.         else if ($this->_compress_type == 'bz2')
  619.             $this->_file = @bzopen($v_filename, "rb");
  620.         else if ($this->_compress_type == 'none')
  621.             $this->_file = @fopen($v_filename, "rb");
  622.         else
  623.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  624.  
  625.         if ($this->_file == 0) {
  626.             $this->_error('Unable to open in read mode \''.$v_filename.'\'');
  627.             return false;
  628.         }
  629.  
  630.         return true;
  631.     }
  632.     // }}}
  633.  
  634.     // {{{ _openReadWrite()
  635.     function _openReadWrite()
  636.     {
  637.         if ($this->_compress_type == 'gz')
  638.             $this->_file = @gzopen($this->_tarname, "r+b");
  639.         else if ($this->_compress_type == 'bz2')
  640.             $this->_file = @bzopen($this->_tarname, "r+b");
  641.         else if ($this->_compress_type == 'none')
  642.             $this->_file = @fopen($this->_tarname, "r+b");
  643.         else
  644.             $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  645.  
  646.         if ($this->_file == 0) {
  647.             $this->_error('Unable to open in read/write mode \''.$this->_tarname.'\'');
  648.             return false;
  649.         }
  650.  
  651.         return true;
  652.     }
  653.     // }}}
  654.  
  655.     // {{{ _close()
  656.     function _close()
  657.     {
  658.         //if (isset($this->_file)) {
  659.         if (is_resource($this->_file)) {
  660.             if ($this->_compress_type == 'gz')
  661.                 @gzclose($this->_file);
  662.             else if ($this->_compress_type == 'bz2')
  663.                 @bzclose($this->_file);
  664.             else if ($this->_compress_type == 'none')
  665.                 @fclose($this->_file);
  666.             else
  667.                 $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  668.  
  669.             $this->_file = 0;
  670.         }
  671.  
  672.         // ----- Look if a local copy need to be erase
  673.         // Note that it might be interesting to keep the url for a time : ToDo
  674.         if ($this->_temp_tarname != '') {
  675.             @unlink($this->_temp_tarname);
  676.             $this->_temp_tarname = '';
  677.         }
  678.  
  679.         return true;
  680.     }
  681.     // }}}
  682.  
  683.     // {{{ _cleanFile()
  684.     function _cleanFile()
  685.     {
  686.         $this->_close();
  687.  
  688.         // ----- Look for a local copy
  689.         if ($this->_temp_tarname != '') {
  690.             // ----- Remove the local copy but not the remote tarname
  691.             @unlink($this->_temp_tarname);
  692.             $this->_temp_tarname = '';
  693.         } else {
  694.             // ----- Remove the local tarname file
  695.             @unlink($this->_tarname);
  696.         }
  697.         $this->_tarname = '';
  698.  
  699.         return true;
  700.     }
  701.     // }}}
  702.  
  703.     // {{{ _writeBlock()
  704.     function _writeBlock($p_binary_data, $p_len=null)
  705.     {
  706.       if (is_resource($this->_file)) {
  707.           if ($p_len === null) {
  708.               if ($this->_compress_type == 'gz')
  709.                   @gzputs($this->_file, $p_binary_data);
  710.               else if ($this->_compress_type == 'bz2')
  711.                   @bzwrite($this->_file, $p_binary_data);
  712.               else if ($this->_compress_type == 'none')
  713.                   @fputs($this->_file, $p_binary_data);
  714.               else
  715.                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  716.           } else {
  717.               if ($this->_compress_type == 'gz')
  718.                   @gzputs($this->_file, $p_binary_data, $p_len);
  719.               else if ($this->_compress_type == 'bz2')
  720.                   @bzwrite($this->_file, $p_binary_data, $p_len);
  721.               else if ($this->_compress_type == 'none')
  722.                   @fputs($this->_file, $p_binary_data, $p_len);
  723.               else
  724.                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  725.  
  726.           }
  727.       }
  728.       return true;
  729.     }
  730.     // }}}
  731.  
  732.     // {{{ _readBlock()
  733.     function _readBlock($p_len=null)
  734.     {
  735.       $v_block = null;
  736.       if (is_resource($this->_file)) {
  737.           if ($p_len === null)
  738.               $p_len = 512;
  739.               
  740.           if ($this->_compress_type == 'gz')
  741.               $v_block = @gzread($this->_file, 512);
  742.           else if ($this->_compress_type == 'bz2')
  743.               $v_block = @bzread($this->_file, 512);
  744.           else if ($this->_compress_type == 'none')
  745.               $v_block = @fread($this->_file, 512);
  746.           else
  747.               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  748.  
  749.       }
  750.       return $v_block;
  751.     }
  752.     // }}}
  753.  
  754.     // {{{ _jumpBlock()
  755.     function _jumpBlock($p_len=null)
  756.     {
  757.       if (is_resource($this->_file)) {
  758.           if ($p_len === null)
  759.               $p_len = 1;
  760.  
  761.           if ($this->_compress_type == 'gz')
  762.               @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
  763.           else if ($this->_compress_type == 'bz2') {
  764.               // ----- Replace missing bztell() and bzseek()
  765.               for ($i=0; $i<$p_len; $i++)
  766.                   $this->_readBlock();
  767.           } else if ($this->_compress_type == 'none')
  768.               @fseek($this->_file, @ftell($this->_file)+($p_len*512));
  769.           else
  770.               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
  771.  
  772.       }
  773.       return true;
  774.     }
  775.     // }}}
  776.  
  777.     // {{{ _writeFooter()
  778.     function _writeFooter()
  779.     {
  780.       if (is_resource($this->_file)) {
  781.           // ----- Write the last 0 filled block for end of archive
  782.           $v_binary_data = pack("a512", '');
  783.           $this->_writeBlock($v_binary_data);
  784.       }
  785.       return true;
  786.     }
  787.     // }}}
  788.  
  789.     // {{{ _addList()
  790.     function _addList($p_list, $p_add_dir, $p_remove_dir)
  791.     {
  792.       $v_result=true;
  793.       $v_header = array();
  794.  
  795.       // ----- Remove potential windows directory separator
  796.       $p_add_dir = $this->_translateWinPath($p_add_dir);
  797.       $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  798.  
  799.       if (!$this->_file) {
  800.           $this->_error('Invalid file descriptor');
  801.           return false;
  802.       }
  803.  
  804.       if (sizeof($p_list) == 0)
  805.           return true;
  806.  
  807.       for ($j=0; ($j<count($p_list)) && ($v_result); $j++) {
  808.         $v_filename = $p_list[$j];
  809.  
  810.         // ----- Skip the current tar name
  811.         if ($v_filename == $this->_tarname)
  812.             continue;
  813.  
  814.         if ($v_filename == '')
  815.             continue;
  816.  
  817.         if (!file_exists($v_filename)) {
  818.             $this->_warning("File '$v_filename' does not exist");
  819.             continue;
  820.         }
  821.  
  822.         // ----- Add the file or directory header
  823.         if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
  824.             return false;
  825.  
  826.         if (@is_dir($v_filename)) {
  827.             if (!($p_hdir = opendir($v_filename))) {
  828.                 $this->_warning("Directory '$v_filename' can not be read");
  829.                 continue;
  830.             }
  831.             $p_hitem = readdir($p_hdir); // '.' directory
  832.             $p_hitem = readdir($p_hdir); // '..' directory
  833.             while (false !== ($p_hitem = readdir($p_hdir))) {
  834.                 if ($v_filename != ".")
  835.                     $p_temp_list[0] = $v_filename.'/'.$p_hitem;
  836.                 else
  837.                     $p_temp_list[0] = $p_hitem;
  838.  
  839.                 $v_result = $this->_addList($p_temp_list, $p_add_dir, $p_remove_dir);
  840.             }
  841.  
  842.             unset($p_temp_list);
  843.             unset($p_hdir);
  844.             unset($p_hitem);
  845.         }
  846.       }
  847.  
  848.       return $v_result;
  849.     }
  850.     // }}}
  851.  
  852.     // {{{ _addFile()
  853.     function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
  854.     {
  855.       if (!$this->_file) {
  856.           $this->_error('Invalid file descriptor');
  857.           return false;
  858.       }
  859.  
  860.       if ($p_filename == '') {
  861.           $this->_error('Invalid file name');
  862.           return false;
  863.       }
  864.  
  865.       // ----- Calculate the stored filename
  866.       $p_filename = $this->_translateWinPath($p_filename, false);;
  867.       $v_stored_filename = $p_filename;
  868.       if (strcmp($p_filename, $p_remove_dir) == 0) {
  869.           return true;
  870.       }
  871.       if ($p_remove_dir != '') {
  872.           if (substr($p_remove_dir, -1) != '/')
  873.               $p_remove_dir .= '/';
  874.  
  875.           if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
  876.               $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  877.       }
  878.       $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  879.       if ($p_add_dir != '') {
  880.           if (substr($p_add_dir, -1) == '/')
  881.               $v_stored_filename = $p_add_dir.$v_stored_filename;
  882.           else
  883.               $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
  884.       }
  885.  
  886.       $v_stored_filename = $this->_pathReduction($v_stored_filename);
  887.  
  888.       if (is_file($p_filename)) {
  889.           if (($v_file = @fopen($p_filename, "rb")) == 0) {
  890.               $this->_warning("Unable to open file '$p_filename' in binary read mode");
  891.               return true;
  892.           }
  893.  
  894.           if (!$this->_writeHeader($p_filename, $v_stored_filename))
  895.               return false;
  896.  
  897.           while (($v_buffer = fread($v_file, 512)) != '') {
  898.               $v_binary_data = pack("a512", "$v_buffer");
  899.               $this->_writeBlock($v_binary_data);
  900.           }
  901.  
  902.           fclose($v_file);
  903.  
  904.       } else {
  905.           // ----- Only header for dir
  906.           if (!$this->_writeHeader($p_filename, $v_stored_filename))
  907.               return false;
  908.       }
  909.  
  910.       return true;
  911.     }
  912.     // }}}
  913.  
  914.     // {{{ _addString()
  915.     function _addString($p_filename, $p_string)
  916.     {
  917.       if (!$this->_file) {
  918.           $this->_error('Invalid file descriptor');
  919.           return false;
  920.       }
  921.  
  922.       if ($p_filename == '') {
  923.           $this->_error('Invalid file name');
  924.           return false;
  925.       }
  926.  
  927.       // ----- Calculate the stored filename
  928.       $p_filename = $this->_translateWinPath($p_filename, false);;
  929.  
  930.       if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), 0, 0, "", 0, 0))
  931.           return false;
  932.  
  933.       $i=0;
  934.       while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
  935.           $v_binary_data = pack("a512", $v_buffer);
  936.           $this->_writeBlock($v_binary_data);
  937.       }
  938.  
  939.       return true;
  940.     }
  941.     // }}}
  942.  
  943.     // {{{ _writeHeader()
  944.     function _writeHeader($p_filename, $p_stored_filename)
  945.     {
  946.         if ($p_stored_filename == '')
  947.             $p_stored_filename = $p_filename;
  948.         $v_reduce_filename = $this->_pathReduction($p_stored_filename);
  949.  
  950.         if (strlen($v_reduce_filename) > 99) {
  951.           if (!$this->_writeLongHeader($v_reduce_filename))
  952.             return false;
  953.         }
  954.  
  955.         $v_info = stat($p_filename);
  956.         $v_uid = sprintf("%6s ", DecOct($v_info[4]));
  957.         $v_gid = sprintf("%6s ", DecOct($v_info[5]));
  958.         $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
  959.  
  960.         $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
  961.  
  962.         if (@is_dir($p_filename)) {
  963.           $v_typeflag = "5";
  964.           $v_size = sprintf("%11s ", DecOct(0));
  965.         } else {
  966.           $v_typeflag = '';
  967.           clearstatcache();
  968.           $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
  969.         }
  970.  
  971.         $v_linkname = '';
  972.  
  973.         $v_magic = '';
  974.  
  975.         $v_version = '';
  976.  
  977.         $v_uname = '';
  978.  
  979.         $v_gname = '';
  980.  
  981.         $v_devmajor = '';
  982.  
  983.         $v_devminor = '';
  984.  
  985.         $v_prefix = '';
  986.  
  987.         $v_binary_data_first = pack("a100a8a8a8a12A12", $v_reduce_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
  988.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  989.  
  990.         // ----- Calculate the checksum
  991.         $v_checksum = 0;
  992.         // ..... First part of the header
  993.         for ($i=0; $i<148; $i++)
  994.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  995.         // ..... Ignore the checksum value and replace it by ' ' (space)
  996.         for ($i=148; $i<156; $i++)
  997.             $v_checksum += ord(' ');
  998.         // ..... Last part of the header
  999.         for ($i=156, $j=0; $i<512; $i++, $j++)
  1000.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1001.  
  1002.         // ----- Write the first 148 bytes of the header in the archive
  1003.         $this->_writeBlock($v_binary_data_first, 148);
  1004.  
  1005.         // ----- Write the calculated checksum
  1006.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1007.         $v_binary_data = pack("a8", $v_checksum);
  1008.         $this->_writeBlock($v_binary_data, 8);
  1009.  
  1010.         // ----- Write the last 356 bytes of the header in the archive
  1011.         $this->_writeBlock($v_binary_data_last, 356);
  1012.  
  1013.         return true;
  1014.     }
  1015.     // }}}
  1016.  
  1017.     // {{{ _writeHeaderBlock()
  1018.     function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, $p_type='', $p_uid=0, $p_gid=0)
  1019.     {
  1020.         $p_filename = $this->_pathReduction($p_filename);
  1021.  
  1022.         if (strlen($p_filename) > 99) {
  1023.           if (!$this->_writeLongHeader($p_filename))
  1024.             return false;
  1025.         }
  1026.  
  1027.         if ($p_type == "5") {
  1028.           $v_size = sprintf("%11s ", DecOct(0));
  1029.         } else {
  1030.           $v_size = sprintf("%11s ", DecOct($p_size));
  1031.         }
  1032.  
  1033.         $v_uid = sprintf("%6s ", DecOct($p_uid));
  1034.         $v_gid = sprintf("%6s ", DecOct($p_gid));
  1035.         $v_perms = sprintf("%6s ", DecOct($p_perms));
  1036.  
  1037.         $v_mtime = sprintf("%11s", DecOct($p_mtime));
  1038.  
  1039.         $v_linkname = '';
  1040.  
  1041.         $v_magic = '';
  1042.  
  1043.         $v_version = '';
  1044.  
  1045.         $v_uname = '';
  1046.  
  1047.         $v_gname = '';
  1048.  
  1049.         $v_devmajor = '';
  1050.  
  1051.         $v_devminor = '';
  1052.  
  1053.         $v_prefix = '';
  1054.  
  1055.         $v_binary_data_first = pack("a100a8a8a8a12A12", $p_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
  1056.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $p_type, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  1057.  
  1058.         // ----- Calculate the checksum
  1059.         $v_checksum = 0;
  1060.         // ..... First part of the header
  1061.         for ($i=0; $i<148; $i++)
  1062.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1063.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1064.         for ($i=148; $i<156; $i++)
  1065.             $v_checksum += ord(' ');
  1066.         // ..... Last part of the header
  1067.         for ($i=156, $j=0; $i<512; $i++, $j++)
  1068.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1069.  
  1070.         // ----- Write the first 148 bytes of the header in the archive
  1071.         $this->_writeBlock($v_binary_data_first, 148);
  1072.  
  1073.         // ----- Write the calculated checksum
  1074.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1075.         $v_binary_data = pack("a8", $v_checksum);
  1076.         $this->_writeBlock($v_binary_data, 8);
  1077.  
  1078.         // ----- Write the last 356 bytes of the header in the archive
  1079.         $this->_writeBlock($v_binary_data_last, 356);
  1080.  
  1081.         return true;
  1082.     }
  1083.     // }}}
  1084.  
  1085.     // {{{ _writeLongHeader()
  1086.     function _writeLongHeader($p_filename)
  1087.     {
  1088.         $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
  1089.  
  1090.         $v_typeflag = 'L';
  1091.  
  1092.         $v_linkname = '';
  1093.  
  1094.         $v_magic = '';
  1095.  
  1096.         $v_version = '';
  1097.  
  1098.         $v_uname = '';
  1099.  
  1100.         $v_gname = '';
  1101.  
  1102.         $v_devmajor = '';
  1103.  
  1104.         $v_devminor = '';
  1105.  
  1106.         $v_prefix = '';
  1107.  
  1108.         $v_binary_data_first = pack("a100a8a8a8a12A12", '././@LongLink', 0, 0, 0, $v_size, 0);
  1109.         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
  1110.  
  1111.         // ----- Calculate the checksum
  1112.         $v_checksum = 0;
  1113.         // ..... First part of the header
  1114.         for ($i=0; $i<148; $i++)
  1115.             $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1116.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1117.         for ($i=148; $i<156; $i++)
  1118.             $v_checksum += ord(' ');
  1119.         // ..... Last part of the header
  1120.         for ($i=156, $j=0; $i<512; $i++, $j++)
  1121.             $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1122.  
  1123.         // ----- Write the first 148 bytes of the header in the archive
  1124.         $this->_writeBlock($v_binary_data_first, 148);
  1125.  
  1126.         // ----- Write the calculated checksum
  1127.         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1128.         $v_binary_data = pack("a8", $v_checksum);
  1129.         $this->_writeBlock($v_binary_data, 8);
  1130.  
  1131.         // ----- Write the last 356 bytes of the header in the archive
  1132.         $this->_writeBlock($v_binary_data_last, 356);
  1133.  
  1134.         // ----- Write the filename as content of the block
  1135.         $i=0;
  1136.         while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
  1137.             $v_binary_data = pack("a512", "$v_buffer");
  1138.             $this->_writeBlock($v_binary_data);
  1139.         }
  1140.  
  1141.         return true;
  1142.     }
  1143.     // }}}
  1144.  
  1145.     // {{{ _readHeader()
  1146.     function _readHeader($v_binary_data, &$v_header)
  1147.     {
  1148.         if (strlen($v_binary_data)==0) {
  1149.             $v_header['filename'] = '';
  1150.             return true;
  1151.         }
  1152.  
  1153.         if (strlen($v_binary_data) != 512) {
  1154.             $v_header['filename'] = '';
  1155.             $this->_error('Invalid block size : '.strlen($v_binary_data));
  1156.             return false;
  1157.         }
  1158.  
  1159.         // ----- Calculate the checksum
  1160.         $v_checksum = 0;
  1161.         // ..... First part of the header
  1162.         for ($i=0; $i<148; $i++)
  1163.             $v_checksum+=ord(substr($v_binary_data,$i,1));
  1164.         // ..... Ignore the checksum value and replace it by ' ' (space)
  1165.         for ($i=148; $i<156; $i++)
  1166.             $v_checksum += ord(' ');
  1167.         // ..... Last part of the header
  1168.         for ($i=156; $i<512; $i++)
  1169.            $v_checksum+=ord(substr($v_binary_data,$i,1));
  1170.  
  1171.         $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $v_binary_data);
  1172.  
  1173.         // ----- Extract the checksum
  1174.         $v_header['checksum'] = OctDec(trim($v_data['checksum']));
  1175.         if ($v_header['checksum'] != $v_checksum) {
  1176.             $v_header['filename'] = '';
  1177.  
  1178.             // ----- Look for last block (empty block)
  1179.             if (($v_checksum == 256) && ($v_header['checksum'] == 0))
  1180.                 return true;
  1181.  
  1182.             $this->_error('Invalid checksum for file "'.$v_data['filename'].'" : '.$v_checksum.' calculated, '.$v_header['checksum'].' expected');
  1183.             return false;
  1184.         }
  1185.  
  1186.         // ----- Extract the properties
  1187.         $v_header['filename'] = trim($v_data['filename']);
  1188.         $v_header['mode'] = OctDec(trim($v_data['mode']));
  1189.         $v_header['uid'] = OctDec(trim($v_data['uid']));
  1190.         $v_header['gid'] = OctDec(trim($v_data['gid']));
  1191.         $v_header['size'] = OctDec(trim($v_data['size']));
  1192.         $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  1193.         if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  1194.           $v_header['size'] = 0;
  1195.         }
  1196.         /* ----- All these fields are removed form the header because they do not carry interesting info
  1197.         $v_header[link] = trim($v_data[link]);
  1198.         $v_header[magic] = trim($v_data[magic]);
  1199.         $v_header[version] = trim($v_data[version]);
  1200.         $v_header[uname] = trim($v_data[uname]);
  1201.         $v_header[gname] = trim($v_data[gname]);
  1202.         $v_header[devmajor] = trim($v_data[devmajor]);
  1203.         $v_header[devminor] = trim($v_data[devminor]);
  1204.         */
  1205.  
  1206.         return true;
  1207.     }
  1208.     // }}}
  1209.  
  1210.     // {{{ _readLongHeader()
  1211.     function _readLongHeader(&$v_header)
  1212.     {
  1213.       $v_filename = '';
  1214.       $n = floor($v_header['size']/512);
  1215.       for ($i=0; $i<$n; $i++) {
  1216.         $v_content = $this->_readBlock();
  1217.         $v_filename .= $v_content;
  1218.       }
  1219.       if (($v_header['size'] % 512) != 0) {
  1220.         $v_content = $this->_readBlock();
  1221.         $v_filename .= $v_content;
  1222.       }
  1223.  
  1224.       // ----- Read the next header
  1225.       $v_binary_data = $this->_readBlock();
  1226.  
  1227.       if (!$this->_readHeader($v_binary_data, $v_header))
  1228.         return false;
  1229.  
  1230.       $v_header['filename'] = $v_filename;
  1231.  
  1232.       return true;
  1233.     }
  1234.     // }}}
  1235.  
  1236.     // {{{ _extractInString()
  1237.     /**
  1238.     * This method extract from the archive one file identified by $p_filename.
  1239.     * The return value is a string with the file content, or NULL on error.
  1240.     * @param string $p_filename     The path of the file to extract in a string.
  1241.     * @return                       a string with the file content or NULL.
  1242.     * @access private
  1243.     */
  1244.     function _extractInString($p_filename)
  1245.     {
  1246.         $v_result_str = "";
  1247.  
  1248.         While (strlen($v_binary_data = $this->_readBlock()) != 0)
  1249.         {
  1250.           if (!$this->_readHeader($v_binary_data, $v_header))
  1251.             return NULL;
  1252.  
  1253.           if ($v_header['filename'] == '')
  1254.             continue;
  1255.  
  1256.           // ----- Look for long filename
  1257.           if ($v_header['typeflag'] == 'L') {
  1258.             if (!$this->_readLongHeader($v_header))
  1259.               return NULL;
  1260.           }
  1261.  
  1262.           if ($v_header['filename'] == $p_filename) {
  1263.               if ($v_header['typeflag'] == "5") {
  1264.                   $this->_error('Unable to extract in string a directory entry {'.$v_header['filename'].'}');
  1265.                   return NULL;
  1266.               } else {
  1267.                   $n = floor($v_header['size']/512);
  1268.                   for ($i=0; $i<$n; $i++) {
  1269.                       $v_result_str .= $this->_readBlock();
  1270.                   }
  1271.                   if (($v_header['size'] % 512) != 0) {
  1272.                       $v_content = $this->_readBlock();
  1273.                       $v_result_str .= substr($v_content, 0, ($v_header['size'] % 512));
  1274.                   }
  1275.                   return $v_result_str;
  1276.               }
  1277.           } else {
  1278.               $this->_jumpBlock(ceil(($v_header['size']/512)));
  1279.           }
  1280.         }
  1281.  
  1282.         return NULL;
  1283.     }
  1284.     // }}}
  1285.  
  1286.     // {{{ _extractList()
  1287.     function _extractList($p_path, &$p_list_detail, $p_mode, $p_file_list, $p_remove_path)
  1288.     {
  1289.     $v_result=true;
  1290.     $v_nb = 0;
  1291.     $v_extract_all = true;
  1292.     $v_listing = false;
  1293.  
  1294.     $p_path = $this->_translateWinPath($p_path, false);
  1295.     if ($p_path == '' || (substr($p_path, 0, 1) != '/' && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
  1296.       $p_path = "./".$p_path;
  1297.     }
  1298.     $p_remove_path = $this->_translateWinPath($p_remove_path);
  1299.  
  1300.     // ----- Look for path to remove format (should end by /)
  1301.     if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
  1302.       $p_remove_path .= '/';
  1303.     $p_remove_path_size = strlen($p_remove_path);
  1304.  
  1305.     switch ($p_mode) {
  1306.       case "complete" :
  1307.         $v_extract_all = TRUE;
  1308.         $v_listing = FALSE;
  1309.       break;
  1310.       case "partial" :
  1311.           $v_extract_all = FALSE;
  1312.           $v_listing = FALSE;
  1313.       break;
  1314.       case "list" :
  1315.           $v_extract_all = FALSE;
  1316.           $v_listing = TRUE;
  1317.       break;
  1318.       default :
  1319.         $this->_error('Invalid extract mode ('.$p_mode.')');
  1320.         return false;
  1321.     }
  1322.  
  1323.     clearstatcache();
  1324.  
  1325.     While (strlen($v_binary_data = $this->_readBlock()) != 0)
  1326.     {
  1327.       $v_extract_file = FALSE;
  1328.       $v_extraction_stopped = 0;
  1329.  
  1330.       if (!$this->_readHeader($v_binary_data, $v_header))
  1331.         return false;
  1332.  
  1333.       if ($v_header['filename'] == '')
  1334.         continue;
  1335.  
  1336.       // ----- Look for long filename
  1337.       if ($v_header['typeflag'] == 'L') {
  1338.         if (!$this->_readLongHeader($v_header))
  1339.           return false;
  1340.       }
  1341.  
  1342.       if ((!$v_extract_all) && (is_array($p_file_list))) {
  1343.         // ----- By default no unzip if the file is not found
  1344.         $v_extract_file = false;
  1345.  
  1346.         for ($i=0; $i<sizeof($p_file_list); $i++) {
  1347.           // ----- Look if it is a directory
  1348.           if (substr($p_file_list[$i], -1) == '/') {
  1349.             // ----- Look if the directory is in the filename path
  1350.             if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) == $p_file_list[$i])) {
  1351.               $v_extract_file = TRUE;
  1352.               break;
  1353.             }
  1354.           }
  1355.  
  1356.           // ----- It is a file, so compare the file names
  1357.           elseif ($p_file_list[$i] == $v_header['filename']) {
  1358.             $v_extract_file = TRUE;
  1359.             break;
  1360.           }
  1361.         }
  1362.       } else {
  1363.         $v_extract_file = TRUE;
  1364.       }
  1365.  
  1366.       // ----- Look if this file need to be extracted
  1367.       if (($v_extract_file) && (!$v_listing))
  1368.       {
  1369.         if (($p_remove_path != '')
  1370.             && (substr($v_header['filename'], 0, $p_remove_path_size) == $p_remove_path))
  1371.           $v_header['filename'] = substr($v_header['filename'], $p_remove_path_size);
  1372.         if (($p_path != './') && ($p_path != '/')) {
  1373.           while (substr($p_path, -1) == '/')
  1374.             $p_path = substr($p_path, 0, strlen($p_path)-1);
  1375.  
  1376.           if (substr($v_header['filename'], 0, 1) == '/')
  1377.               $v_header['filename'] = $p_path.$v_header['filename'];
  1378.           else
  1379.             $v_header['filename'] = $p_path.'/'.$v_header['filename'];
  1380.         }
  1381.         if (file_exists($v_header['filename'])) {
  1382.           if ((@is_dir($v_header['filename'])) && ($v_header['typeflag'] == '')) {
  1383.             $this->_error('File '.$v_header['filename'].' already exists as a directory');
  1384.             return false;
  1385.           }
  1386.           if ((is_file($v_header['filename'])) && ($v_header['typeflag'] == "5")) {
  1387.             $this->_error('Directory '.$v_header['filename'].' already exists as a file');
  1388.             return false;
  1389.           }
  1390.           if (!is_writeable($v_header['filename'])) {
  1391.             $this->_error('File '.$v_header['filename'].' already exists and is write protected');
  1392.             return false;
  1393.           }
  1394.           if (filemtime($v_header['filename']) > $v_header['mtime']) {
  1395.             // To be completed : An error or silent no replace ?
  1396.           }
  1397.         }
  1398.  
  1399.         // ----- Check the directory availability and create it if necessary
  1400.         elseif (($v_result = $this->_dirCheck(($v_header['typeflag'] == "5"?$v_header['filename']:dirname($v_header['filename'])))) != 1) {
  1401.             $this->_error('Unable to create path for '.$v_header['filename']);
  1402.             return false;
  1403.         }
  1404.  
  1405.         if ($v_extract_file) {
  1406.           if ($v_header['typeflag'] == "5") {
  1407.             if (!@file_exists($v_header['filename'])) {
  1408.                 if (!@mkdir($v_header['filename'], 0777)) {
  1409.                     $this->_error('Unable to create directory {'.$v_header['filename'].'}');
  1410.                     return false;
  1411.                 }
  1412.             }
  1413.           } else {
  1414.               if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
  1415.                   $this->_error('Error while opening {'.$v_header['filename'].'} in write binary mode');
  1416.                   return false;
  1417.               } else {
  1418.                   $n = floor($v_header['size']/512);
  1419.                   for ($i=0; $i<$n; $i++) {
  1420.                       $v_content = $this->_readBlock();
  1421.                       fwrite($v_dest_file, $v_content, 512);
  1422.                   }
  1423.             if (($v_header['size'] % 512) != 0) {
  1424.               $v_content = $this->_readBlock();
  1425.               fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
  1426.             }
  1427.  
  1428.             @fclose($v_dest_file);
  1429.  
  1430.             // ----- Change the file mode, mtime
  1431.             @touch($v_header['filename'], $v_header['mtime']);
  1432.             // To be completed
  1433.             //chmod($v_header[filename], DecOct($v_header[mode]));
  1434.           }
  1435.  
  1436.           // ----- Check the file size
  1437.           clearstatcache();
  1438.           if (filesize($v_header['filename']) != $v_header['size']) {
  1439.               $this->_error('Extracted file '.$v_header['filename'].' does not have the correct file size \''.filesize($v_filename).'\' ('.$v_header['size'].' expected). Archive may be corrupted.');
  1440.               return false;
  1441.           }
  1442.           }
  1443.         } else {
  1444.           $this->_jumpBlock(ceil(($v_header['size']/512)));
  1445.         }
  1446.       } else {
  1447.           $this->_jumpBlock(ceil(($v_header['size']/512)));
  1448.       }
  1449.  
  1450.       /* TBC : Seems to be unused ...
  1451.       if ($this->_compress)
  1452.         $v_end_of_file = @gzeof($this->_file);
  1453.       else
  1454.         $v_end_of_file = @feof($this->_file);
  1455.         */
  1456.  
  1457.       if ($v_listing || $v_extract_file || $v_extraction_stopped) {
  1458.         // ----- Log extracted files
  1459.         if (($v_file_dir = dirname($v_header['filename'])) == $v_header['filename'])
  1460.           $v_file_dir = '';
  1461.         if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
  1462.           $v_file_dir = '/';
  1463.  
  1464.         $p_list_detail[$v_nb++] = $v_header;
  1465.       }
  1466.     }
  1467.  
  1468.         return true;
  1469.     }
  1470.     // }}}
  1471.  
  1472.     // {{{ _openAppend()
  1473.     function _openAppend()
  1474.     {
  1475.         if (filesize($this->_tarname) == 0)
  1476.           return $this->_openWrite();
  1477.           
  1478.         if ($this->_compress) {
  1479.             $this->_close();
  1480.  
  1481.             if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
  1482.                 $this->_error('Error while renaming \''.$this->_tarname.'\' to temporary file \''.$this->_tarname.'.tmp\'');
  1483.                 return false;
  1484.             }
  1485.  
  1486.             if ($this->_compress_type == 'gz')
  1487.                 $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
  1488.             elseif ($this->_compress_type == 'bz2')
  1489.                 $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
  1490.                 
  1491.             if ($v_temp_tar == 0) {
  1492.                 $this->_error('Unable to open file \''.$this->_tarname.'.tmp\' in binary read mode');
  1493.                 @rename($this->_tarname.".tmp", $this->_tarname);
  1494.                 return false;
  1495.             }
  1496.  
  1497.             if (!$this->_openWrite()) {
  1498.                 @rename($this->_tarname.".tmp", $this->_tarname);
  1499.                 return false;
  1500.             }
  1501.  
  1502.             if ($this->_compress_type == 'gz') {
  1503.                 $v_buffer = @gzread($v_temp_tar, 512);
  1504.  
  1505.                 // ----- Read the following blocks but not the last one
  1506.                 if (!@gzeof($v_temp_tar)) {
  1507.                     do{
  1508.                         $v_binary_data = pack("a512", $v_buffer);
  1509.                         $this->_writeBlock($v_binary_data);
  1510.                         $v_buffer = @gzread($v_temp_tar, 512);
  1511.  
  1512.                     } while (!@gzeof($v_temp_tar));
  1513.                 }
  1514.  
  1515.                 @gzclose($v_temp_tar);
  1516.             }
  1517.             elseif ($this->_compress_type == 'bz2') {
  1518.                 $v_buffered_lines   = array();
  1519.                 $v_buffered_lines[] = @bzread($v_temp_tar, 512);
  1520.  
  1521.                 // ----- Read the following blocks but not the last one
  1522.                 while (strlen($v_buffered_lines[] = @bzread($v_temp_tar, 512)) > 0) {
  1523.                     $v_binary_data = pack("a512", array_shift($v_buffered_lines));
  1524.                     $this->_writeBlock($v_binary_data);
  1525.                 }
  1526.  
  1527.                 @bzclose($v_temp_tar);
  1528.             }
  1529.  
  1530.             if (!@unlink($this->_tarname.".tmp")) {
  1531.                 $this->_error('Error while deleting temporary file \''.$this->_tarname.'.tmp\'');
  1532.             }
  1533.  
  1534.         } else {
  1535.             // ----- For not compressed tar, just add files before the last 512 bytes block
  1536.             if (!$this->_openReadWrite())
  1537.                return false;
  1538.  
  1539.             clearstatcache();
  1540.             $v_size = filesize($this->_tarname);
  1541.             fseek($this->_file, $v_size-512);
  1542.         }
  1543.  
  1544.         return true;
  1545.     }
  1546.     // }}}
  1547.  
  1548.     // {{{ _append()
  1549.     function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
  1550.     {
  1551.         if (!$this->_openAppend())
  1552.             return false;
  1553.             
  1554.         if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
  1555.            $this->_writeFooter();
  1556.  
  1557.         $this->_close();
  1558.  
  1559.         return true;
  1560.     }
  1561.     // }}}
  1562.  
  1563.     // {{{ _dirCheck()
  1564.  
  1565.     /**
  1566.      * Check if a directory exists and create it (including parent
  1567.      * dirs) if not.
  1568.      *
  1569.      * @param string $p_dir directory to check
  1570.      *
  1571.      * @return bool TRUE if the directory exists or was created
  1572.      */
  1573.     function _dirCheck($p_dir)
  1574.     {
  1575.         if ((@is_dir($p_dir)) || ($p_dir == ''))
  1576.             return true;
  1577.  
  1578.         $p_parent_dir = dirname($p_dir);
  1579.  
  1580.         if (($p_parent_dir != $p_dir) &&
  1581.             ($p_parent_dir != '') &&
  1582.             (!$this->_dirCheck($p_parent_dir)))
  1583.              return false;
  1584.  
  1585.         if (!@mkdir($p_dir, 0777)) {
  1586.             $this->_error("Unable to create directory '$p_dir'");
  1587.             return false;
  1588.         }
  1589.  
  1590.         return true;
  1591.     }
  1592.  
  1593.     // }}}
  1594.  
  1595.     // {{{ _pathReduction()
  1596.  
  1597.     /**
  1598.      * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", and
  1599.      * remove double slashes.
  1600.      *
  1601.      * @param string $p_dir path to reduce
  1602.      *
  1603.      * @return string reduced path
  1604.      *
  1605.      * @access private
  1606.      *
  1607.      */
  1608.     function _pathReduction($p_dir)
  1609.     {
  1610.         $v_result = '';
  1611.  
  1612.         // ----- Look for not empty path
  1613.         if ($p_dir != '') {
  1614.             // ----- Explode path by directory names
  1615.             $v_list = explode('/', $p_dir);
  1616.  
  1617.             // ----- Study directories from last to first
  1618.             for ($i=sizeof($v_list)-1; $i>=0; $i--) {
  1619.                 // ----- Look for current path
  1620.                 if ($v_list[$i] == ".") {
  1621.                     // ----- Ignore this directory
  1622.                     // Should be the first $i=0, but no check is done
  1623.                 }
  1624.                 else if ($v_list[$i] == "..") {
  1625.                     // ----- Ignore it and ignore the $i-1
  1626.                     $i--;
  1627.                 }
  1628.                 else if (($v_list[$i] == '') && ($i!=(sizeof($v_list)-1)) && ($i!=0)) {
  1629.                     // ----- Ignore only the double '//' in path,
  1630.                     // but not the first and last /
  1631.                 } else {
  1632.                     $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'.$v_result:'');
  1633.                 }
  1634.             }
  1635.         }
  1636.         $v_result = strtr($v_result, '\\', '/');
  1637.         return $v_result;
  1638.     }
  1639.  
  1640.     // }}}
  1641.  
  1642.     // {{{ _translateWinPath()
  1643.     function _translateWinPath($p_path, $p_remove_disk_letter=true)
  1644.     {
  1645.       if (OS_WINDOWS) {
  1646.           // ----- Look for potential disk letter
  1647.           if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
  1648.               $p_path = substr($p_path, $v_position+1);
  1649.           }
  1650.           // ----- Change potential windows directory separator
  1651.           if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
  1652.               $p_path = strtr($p_path, '\\', '/');
  1653.           }
  1654.       }
  1655.       return $p_path;
  1656.     }
  1657.     // }}}
  1658.  
  1659. }
  1660. ?>
  1661.